from google.appengine.ext import db

class Requirer(object):
  """Base class for dynamic `required` property attribute."""
  
  def __nonzero__(self):
    """Have default validators omit presence validation of a property."""
    return False
    
  def validate(self, property, key, value, entity):
    if self.is_property_empty(property, value):
      raise db.BadValueError('This field is required.')
    return value
        
  @staticmethod
  def is_property_empty(property, property_value):
    if hasattr(property, 'empty'):
      return property.empty(property_value)
    return not property_value
    
  @staticmethod
  def get_property_label(property):
    return property.verbose_name or property.name.replace('_', ' ').title()
    
class PropertyRequirer(Requirer):
  """Validate a property using the value of another one instead if empty."""  
  
  def __init__(self, other_property_name):
    self._other_property_name = other_property_name
  
  @property
  def other_property_name(self):
    return self._other_property_name
    
  def validate(self, property, key, value, entity):
    """Assert the presence of either value of the defined property."""
    if property.empty(value):
      if self.is_property_empty(self.get_other_property(entity), self.get_other_property_value(entity)):
        raise db.BadValueError('This field is required.')
    return value
  
  def get_other_property(self, entity):
    return getattr(entity.__class__, self.other_property_name)
    
  def get_other_property_value(self, entity):
    return getattr(entity, self.other_property_name)

class NoValuePropertyRequirer(PropertyRequirer):
  """Only allow a property to be set when another one is empty."""  
    
  def validate(self, property, key, value, entity):
    """Assert the presence of only either value."""
    if not self.is_property_empty(self.get_other_property(entity), self.get_other_property_value(entity)) and not self.is_property_empty(property, value):
      raise db.BadValueError('Either this field or %s must be unset.' % self.get_property_label(self.get_other_property(entity)))
    return value
